# -*- coding: UTF-8 -*-
'''
@Project ：push_rk
@File ：mytorchdemo.py
@IDE ：PyCharm
@Author ：苦瓜
@Date ：2025/10/11 10:24
@Note: Something beautiful is about to happen !
'''
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import numpy as np
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split

# StockDailyPredictionRNN
# 一、项目背景
# 在金融市场中，股票价格预测一直是研究的热点。有效的股票价格预测模型可以帮助投资者做出更明智的投资决策，规避风险，并优化资产配置。然而，股票价格受到多种因素的影响，包括市场情绪、宏观经济状况、公司业绩等，因此其波动具有高度的复杂性和不确定性。传统的金融时间序列分析方法，如ARIMA模型，在处理非线性关系时存在一定的局限性。近年来，深度学习技术，尤其是循环神经网络（RNN），因其强大的序列数据处理能力，在金融时间序列预测中展现出巨大的潜力。
# 二、项目目标
# 1.	数据处理：从提供的data-02-stock_daily.csv数据集中加载股票日收盘价数据，并进行预处理，包括数据归一化和构建适合RNN模型输入的序列数据。
# 2.	模型构建：设计一个包含两层RNN和一个全连接层的神经网络模型，用于学习股票价格的时序特征，并进行未来价格的预测。
# 3.	模型训练：使用均方误差（MSE）作为损失函数，通过反向传播算法和Adam优化器对模型进行训练，以最小化预测误差。
# 4.	性能评估：将训练好的模型应用于测试集，评估其预测性能，并通过可视化预测结果与实际股票价格的对比，验证模型的有效性。
# 5.	应用探索：探索该模型在实际投资决策中的潜在应用，以及进一步改进模型性能的可能方向。
# 三、功能需求
# 1.	数据加载与预处理：
# 系统应能够从指定的CSV文件中加载股票日收盘价数据。
data = np.loadtxt('data-02-stock_daily.csv', delimiter=",")
# 加载的数据需要进行反转，以确保时间顺序的正确性。
data = data[::-1]
# 数据应经过归一化处理，以便模型训练。
data = MinMaxScaler().fit_transform(data)
# 需要构建适合RNN模型输入的序列数据，包括输入特征（x）和对应的目标值（y）。
c = 7
x_list = []
y_list = []
for i in range(len(data) - c):
    x_list.append(data[i:i+c])
    y_list.append(data[i+c, -1:])

# 修改数据类型
X = torch.tensor(x_list, dtype=torch.float32)
y = torch.tensor(y_list, dtype=torch.float32)

# 切分数据
X_train, X_test, y_train, y_test = train_test_split(X, y, shuffle=False, random_state=42)

print(f"""
    X_train shape: {X_train.shape}
    X_test shape: {X_test.shape}
    y_train shape: {y_train.shape}
    y_test shape: {y_test.shape}
""")

# 2.	模型构建：
# 应构建RNN神经网络模型。
class MyRNN(nn.Module):

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self.rnn1 = nn.RNN(input_size=X_train.shape[2], hidden_size=10, batch_first=True)
        self.rnn2 = nn.RNN(input_size=10, hidden_size=10, batch_first=True)
        self.fc = nn.Linear(in_features=10, out_features=1)

    def forward(self, inputs):
        x, _ = self.rnn1(inputs)
        x, _ = self.rnn2(x)
        x = self.fc(x[:, -1, :])
        return x


if __name__ == '__main__':
    # 3.	模型训练：
    model = MyRNN()
    # 使用均方误差（MSE）作为损失函数。
    cost = nn.MSELoss()
    # 通过反向传播算法和Adam优化器对模型进行训练。
    optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
    losses_ = []
    model.train()
    for epoch in range(1000):
        optimizer.zero_grad()
        y_pred_train = model(X_train)
        loss_ = cost(y_pred_train, y_train)
        losses_.append(loss_.item())
        loss_.backward()
        optimizer.step()
        if epoch % 10 == 0 :
            print(f"<Model train {epoch} loss : ({loss_})>")
    # 训练过程中应记录损失值，以便后续分析。

    model.eval()
    with torch.no_grad():
        y_pred_test = model(X_test)
    # 4.	性能评估：
    # 训练好的模型应在测试集上进行预测。
    # 预测结果应与实际股票价格进行对比，以评估模型性能。
    # 预测结果和实际股票价格应通过可视化方式展示。
    plt.plot(y_pred_test, label="y pred test", c="g")
    plt.plot(y_test, label="y test", c="r")
    plt.legend()
    plt.show()

    plt.plot(losses_, label="loss")
    plt.legend()
    plt.title("loss")
    plt.show()

    # 5.	其他要求：
    # 代码应具有可读性，变量和函数命名应清晰明了。
    # 应提供必要的注释，以解释代码的功能和逻辑。
    # 代码应能够在一个标准的Python环境中运行，依赖的库应通过常规方式安装。
    # 模型调优，考题中要求使用体现不同优化方案的要求，比如超参数方面的，比如说分类精度低的数据集的增强；比如说调整网络结构，比如说调整优化器学习率等；不能只是增加epoch大小，或者调整batch_size就可以，必须采用两种或者两种以上的调优方法进行调优，最终结论要给出综合各种方法调优得出的综合结论
    """
    模型调优：
        使用Adam优化器时：<Model train 990 loss : (0.0010497879702597857)>
        使用RMSprop优化器时：<Model train 990 loss : (0.001058374298736453)>
        使用 Adagrad 优化器时：<Model train 990 loss : (0.002671643393114209)>
        
        使用Adam优化器时，学习率为0.001: <Model train 990 loss : (0.0010497879702597857)>
        使用Adam优化器时，学习率为0.005: <Model train 990 loss : (0.0007970965816639364)>
        使用Adam优化器时，学习率为0.01:  <Model train 990 loss : (0.0007678359397687018)>
    """